///////////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2007-2009 ILOG, S.A.
//  All Rights Reserved.
//  The following is ILOG Source Code.  No modifications may be made to the  
//  ILOG Source Code.  Any usage of the ILOG Source Code is subject to 
//  the terms and conditions of the ILOG End User License Agreement   
//  applicable to this ILOG software product.
//
///////////////////////////////////////////////////////////////////////////////

package ilog.gauges.circular
{
  import flash.events.Event;
  
  import ilog.gauges.gaugesClasses.CategoryScale;
  import ilog.gauges.gaugesClasses.GaugeElement;
  import ilog.gauges.gaugesClasses.circular.ICircularScale;
  import ilog.utils.MathUtil;

  /**
   * The <code>CircularCategoryScale</code> class represents the category scale for 
   * circular gauges. This class can be used to populate the <i>scales</i> array of 
   * the gauge. Categories are defined by the <i>categories</i> array. 
   *  
   * @see ilog.gauges.circular.CircularGauge
   * @includeExample examples/CircularCategoryScaleExample.mxml -noswf 
   */
  public class CircularCategoryScale extends CategoryScale implements ICircularScale
  {
    /**
     * Constructor
     */
    public function CircularCategoryScale()
    {
      super();
    }

    /**
     *  @private
     */
    private var _startAngleRadians:Number;

    /**
     *  @private
     */
    private var _endAngleRadians:Number;

    /**
     *  @private
     */
    private var _totalAngleRadians:Number;

    /**
     *  @private
     */
    private var _startAngle:Number = 210;

    [Bindable("startAngleChanged")]
    [Inspectable(category="Gauge")]    

    /**
     *  @copy ilog.gauges.gaugesClasses.circular.ICircularScale#startAngle
     */
    public function get startAngle():Number
    {
      return _startAngle;
    }

    /**
     *  @private
     */
    public function set startAngle(value:Number):void
    {
      _startAngleRadians = MathUtil.mod2PI(MathUtil.toRadians(value));
      computeTotalAngleRadians();
      _startAngle = value;
      invalidateTicks();
      dispatchEvent(new Event("startAngleChanged"));      
    }

    /**
     *  @private
     */
    private var _endAngle:Number = 330;

    [Bindable("endAngleChanged")]
    [Inspectable(category="Gauge")] 
    
    /**
     *  @copy ilog.gauges.gaugesClasses.circular.ICircularScale#endAngle
     */
    public function get endAngle():Number
    {
      return _endAngle;
    }

    /**
     *  @private
     */
    public function set endAngle(value:Number):void
    {
      _endAngleRadians = MathUtil.mod2PI(MathUtil.toRadians(value));
      computeTotalAngleRadians();
      _endAngle = value;
      invalidateTicks();
      dispatchEvent(new Event("endAngleChanged"));
    }

    /**
     *  @private
     */
    private var _orientation:String = "clockwise";

    [Bindable("orientationChanged")]
    [Inspectable(category="Gauge", enumeration="clockwise,cclockwise")]
    
    /**
     *  @copy ilog.gauges.gaugesClasses.circular.ICircularScale#orientation
     */
    public function get orientation():String
    {
      return _orientation;
    }

    /**
     *  @private
     */
    public function set orientation(value:String):void
    {
      _orientation = value;
      computeTotalAngleRadians();
      invalidateTicks(); 
      dispatchEvent(new Event("orientationChanged"));      
    }

    /**
     *  @private
     */
    private function computeTotalAngleRadians():void
    {
      if (_startAngleRadians == _endAngleRadians)
        _totalAngleRadians = 2 * Math.PI;
      else
        _totalAngleRadians = computeAngle(_startAngleRadians, _endAngleRadians, orientation); 
    }

    /**
     *  @private
     */
    private function computeAngle(startAngle:Number, endAngle:Number, orientation:String):Number
    {
      var totalAngle:Number;
      if (endAngle == startAngle)
        totalAngle = 2 * Math.PI;
    
      if (orientation == "cclockwise") {
        if (endAngle < startAngle)
          totalAngle = 2 * Math.PI - (startAngle - endAngle);
        else
          totalAngle = endAngle - startAngle;
      } else {
        if (endAngle < startAngle)
          totalAngle = startAngle - endAngle;
        else
          totalAngle = 2 * Math.PI - (endAngle - startAngle);
      }
      
      return totalAngle;
    }

    /**
     *  @copy ilog.gauges.gaugesClasses.circular.ICircularScale#angleForValue()
     */
    public function angleForValue(value:Object):Number
    {
      if (value == null)
        return _startAngleRadians;
        
      var v:String = String(value);
     
      var i:int = getCategoryIndex(v);
  
      if (i != -1) {
        var startAngleRadians:Number = MathUtil.toRadians(startAngle);
        var endAngleRadians:Number = MathUtil.toRadians(endAngle);
        var totalAngle:Number = computeAngle(startAngleRadians, endAngleRadians, orientation);
        var d:Number = orientation=="clockwise"?-1:1;
        return MathUtil.mod2PI(startAngleRadians + d * totalAngle * majorTickItems[i].position);
      } else
        return _startAngleRadians;    
    }

    /**
     *  @copy ilog.gauges.gaugesClasses.circular.ICircularScale#valueForAngle()
     */
    public function valueForAngle(value:Number):Object
    {
      if (!angleInRange(value)) {
        var min1:Number = MathUtil.mod2PI(_startAngleRadians - value);
        var min2:Number = 2 * Math.PI - min1;
        var max1:Number = MathUtil.mod2PI(_endAngleRadians - value);
        var max2:Number = 2 * Math.PI - max1;
        if (Math.min(min1, min2) < Math.min(max1, max2))
          return categories[0];
        else return categories[categories.length-1];
      } else {
        var maximum:Number = categories.length;
        var range:Number = maximum-1;
        var _orientationNum:Number = orientation=="clockwise"?-1:1;        
        var relativeAngle:Number = MathUtil.mod2PI(_orientationNum * (value - _startAngleRadians));
        var v:Number = Math.round((categories.length-1) * relativeAngle/_totalAngleRadians);
        return categories[v];
      }
    }
    /**
     *  @copy ilog.gauges.gaugesClasses.circular.ICircularScale#angleInRange()
     */
    public function angleInRange(value:Number):Boolean
    {
      if (startAngle == endAngle)
        return true;
      value = MathUtil.mod2PI(value);
      var _orientationNum:Number = orientation=="clockwise"?-1:1;
      if (_orientationNum == 1) {
        if (_startAngleRadians < _endAngleRadians)
          return value >= _startAngleRadians && value <= _endAngleRadians;
        else
          return ! (value > _endAngleRadians && value < _startAngleRadians);
      } else {
        if (_startAngleRadians < _endAngleRadians)
          return ! (value > _startAngleRadians && value < _endAngleRadians);
        else
          return value >= _endAngleRadians && value <= _startAngleRadians;
      }
    }
    
    /**
     * @copy ilog.gauges.gaugesClasses.IScale#invalidateElements()
     */
    override public function invalidateElements():void
    {
      super.invalidateElements();
      for each (var element:GaugeElement in elements) {
        element.invalidateProperties();
        element.invalidateSize();
      }
    }    
  }
}
